//
//  overlayWindow.m
//  overlayWindow
//
//  Created by Daniel Hazelbaker on 1/20/06.
//  Copyright 2006 Blue Box Moon. All rights reserved.
//

#import "overlayWindow.h"
#import "overlayView.h"


@implementation overlayWindow


//
// Create a new, generic overlay window on the main screen.
//
+ (id)overlayWindow
{
    return [self overlayWindowOnScreen:[NSScreen mainScreen]];
}


//
// Create a new, generic overlay window on the given screen.
//
+ (id)overlayWindowOnScreen:(NSScreen *)screen
{
    NSRect	rect = NSMakeRect(0, 0, 220, 220);


    if ([super init] == nil)
	return nil;

    return [[[self alloc] initWithContentRect:rect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO screen:screen] autorelease];
}


//
// Create a new overlay window
//
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag
{
    //
    // Create the window.
    //
    if ([super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO] == nil)
	return nil;

    //
    // Setup the window.
    //
    [self setHidesOnDeactivate:NO];
    [self setBecomesKeyOnlyIfNeeded:YES];
    [self setLevel:NSStatusWindowLevel];
    [self setBackgroundColor:[NSColor clearColor]];
    [self setOpaque:NO];
    if ([[self contentView] isKindOfClass:[overlayView class]] == NO)
	[self setContentView:[[[overlayView alloc] initWithFrame:NSMakeRect(0, 0, contentRect.size.width, contentRect.size.height)] autorelease]];

    //
    // Set defaults.
    //
    [self setVisibleTime:1.5];
    [self setCrawlSpeed:15];

    return self;
}


//
// Free memory
//
- (void)dealloc
{
    if (timer != nil) {
	[timer invalidate];
	timer = nil;
    }

    [super dealloc];
}


//
// The window has been activated. Notify everybody.
//
- (void)activated
{
    if (timer != nil) {
	[timer invalidate];
	timer = nil;
    }

    //
    // Make sure window is in its final position.
    //
    [self setFrame:finalPosition display:NO];

    //
    // Start a timer to deactivate the window.
    //
    if ([self visibleTime] > 0)
	timer = [NSTimer scheduledTimerWithTimeInterval:[self visibleTime] target:self selector:@selector(deactivateTimer:) userInfo:nil repeats:NO];

    //
    // Notify observers that we are active.
    //
    [[NSNotificationCenter defaultCenter] postNotificationName:BMOverlayWindowDidActivate object:self userInfo:nil];
}


//
// The window has been deactivated. Notify everybody.
//
- (void)deactivated
{
    //
    // Make sure the window is completely invisible.
    //
    [self orderOut:self];

    //
    // Notify observers that we are inactive.
    //
    [[NSNotificationCenter defaultCenter] postNotificationName:BMOverlayWindowDidDeactivate object:self userInfo:nil];
}


//
// Activate the overlay window.
//
- (void)activate:(id)sender
{
    BOOL    activated = NO;


    //
    // Notify observers that we are about to go active.
    //
    [[NSNotificationCenter defaultCenter] postNotificationName:BMOverlayWindowWillActivate object:self userInfo:nil];

    [self performPlacement];

    //
    // Check if we are going to be crawling in or not.
    //
    if ([self crawlsIn] && (placement == BMOverlayPlacementTopTight || placement == BMOverlayPlacementBottomTight ||
	placement == BMOverlayPlacementLeftTight || placement == BMOverlayPlacementRightTight ||
	placement == BMOverlayPlacementTopLeftTight || placement == BMOverlayPlacementTopRightTight ||
	placement == BMOverlayPlacementBottomLeftTight || placement == BMOverlayPlacementBottomRightTight)) {
	//
	// We are going to crawl in, see from which direction and set initial position.
	//
	if (placement == BMOverlayPlacementTopTight)
	    [self setFrame:NSMakeRect(finalPosition.origin.x, finalPosition.origin.y + finalPosition.size.height, finalPosition.size.width, 1) display:NO];
	else if (placement == BMOverlayPlacementBottomTight)
	    [self setFrame:NSMakeRect(finalPosition.origin.x, finalPosition.origin.y, finalPosition.size.width, 1) display:NO];
	else if (placement == BMOverlayPlacementLeftTight || placement == BMOverlayPlacementTopLeftTight ||
		placement == BMOverlayPlacementBottomLeftTight)
	    [self setFrame:NSMakeRect(finalPosition.origin.x, finalPosition.origin.y, 1, finalPosition.size.height) display:NO];
	else // right side
	    [self setFrame:NSMakeRect(finalPosition.origin.x + finalPosition.size.width, finalPosition.origin.y, 1, finalPosition.size.height) display:NO];

	timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(crawlingInTimer:) userInfo:nil repeats:YES];
    }
    else {
	[self setFrame:finalPosition display:NO];
	activated = YES;
    }

    //
    // Make sure the window is visible.
    //
    [self orderFront:self];
    [self display];

    //
    // If the window is fully activated, perform the activated actions.
    //
    if (activated)
	[self activated];
}


//
// Deactivate the window
//
- (void)deactivate:(id)sender
{
    //
    // Notify observers that we are going inactive.
    //
    [[NSNotificationCenter defaultCenter] postNotificationName:BMOverlayWindowWillDeactivate object:self userInfo:nil];

    //
    // Make sure we don't get caught in the middle of something.
    //
    if (timer != nil) {
	[timer invalidate];
	timer = nil;
    }

    //
    // Once we start deactivating the user can no longer interact with us.
    //
    [self setIgnoresMouseEvents:YES];

    //
    // Check if we are going to be crawling out or not.
    //
    if ([self crawlsOut] && (placement == BMOverlayPlacementTopTight || placement == BMOverlayPlacementBottomTight ||
	placement == BMOverlayPlacementLeftTight || placement == BMOverlayPlacementRightTight ||
	placement == BMOverlayPlacementTopLeftTight || placement == BMOverlayPlacementTopRightTight ||
	placement == BMOverlayPlacementBottomLeftTight || placement == BMOverlayPlacementBottomRightTight)) {
	timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(crawlingOutTimer:) userInfo:nil repeats:YES];
    }
    else
	timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(fadingTimer:) userInfo:nil repeats:YES];
}


//
// This timer tells us that it is time for the window to go away.
//
- (void)deactivateTimer:(NSTimer *)aTimer
{
    [self deactivate:self];
}


//
// This timer helps us fade out nicely.
//
- (void)fadingTimer:(NSTimer *)aTimer
{
    float   alpha = [self alphaValue];


    //
    // Check if we are done.
    //
    if (alpha <= 0.0f) {
	[timer invalidate];
	timer = nil;
	[self deactivated];

	return;
    }

    //
    // Take away a tick of visibility.
    //
    alpha -= 0.05;
    if (alpha <= 0.0f)
	alpha = 0.0f;
    [self setAlphaValue:alpha];
    [self display];
}


//
// This timer is used to crawl the window in bit by bit.
//
- (void)crawlingInTimer:(NSTimer *)aTimer
{
    NSRect  rect = [self frame];
    float   amount;


    //
    // See if we are done yet.
    //
    if (finalPosition.origin.x >= (rect.origin.x - 1) && finalPosition.origin.x <= (rect.origin.x + 1) &&
	finalPosition.origin.y >= (rect.origin.y - 1) && finalPosition.origin.y <= (rect.origin.y + 1) &&
	finalPosition.size.width >= (rect.size.width - 1) && finalPosition.size.width <= (rect.size.width + 1) &&
	finalPosition.size.height >= (rect.size.height - 1) && finalPosition.size.height <= (rect.size.height + 1)) {
	//
	// Set the final position.
	//
	[timer invalidate];
	timer = nil;
	[self activated];

	return;
    }

    //
    // See which way we are crawling and make the movement.
    //
    if (placement == BMOverlayPlacementTopTight) {
	amount = (rect.origin.y - finalPosition.origin.y) / [self crawlSpeed];
	rect.origin.y -= amount;
	rect.size.height += amount;
    }
    else if (placement == BMOverlayPlacementBottomTight) {
	amount = (finalPosition.size.height - rect.size.height) / [self crawlSpeed];
	rect.size.height += amount;
    }
    else if (placement == BMOverlayPlacementLeftTight || placement == BMOverlayPlacementTopLeftTight ||
	    placement == BMOverlayPlacementBottomLeftTight) {
	amount = (finalPosition.size.width - rect.size.width) / [self crawlSpeed];
	rect.size.width += amount;
    }
    else {// right side
	amount = (rect.origin.x - finalPosition.origin.x) / [self crawlSpeed];
	rect.origin.x -= amount;
	rect.size.width += amount;
    }

    //
    // Set the new position.
    //
    [self setFrame:rect display:YES];
}


//
// This timer is used to crawl the window in bit by bit.
//
- (void)crawlingOutTimer:(NSTimer *)aTimer
{
    NSRect  rect = [self frame];
    float   amount;


    //
    // See if we are done yet.
    //
    if (rect.size.width <= 1 || rect.size.height <= 1) {
	[timer invalidate];
	timer = nil;
	[self deactivated];

	return;
    }

    //
    // See which way we are crawling and make the movement.
    //
    if (placement == BMOverlayPlacementTopTight) {
	amount = (rect.origin.y - finalPosition.origin.y) / [self crawlSpeed];
	if (amount < 1)
	    amount = 1;
	rect.origin.y += amount;
	rect.size.height -= amount;
    }
    else if (placement == BMOverlayPlacementBottomTight) {
	amount = (finalPosition.size.height - rect.size.height) / [self crawlSpeed];
	if (amount < 1)
	    amount = 1;
	rect.size.height -= amount;
    }
    else if (placement == BMOverlayPlacementLeftTight || placement == BMOverlayPlacementTopLeftTight ||
	    placement == BMOverlayPlacementBottomLeftTight) {
	amount = (finalPosition.size.width - rect.size.width) / [self crawlSpeed];
	if (amount < 1)
	    amount = 1;
	rect.size.width -= amount;
    }
    else {// right side
	amount = (rect.origin.x - finalPosition.origin.x) / [self crawlSpeed];
	if (amount < 1)
	    amount = 1;
	rect.origin.x += amount;
	rect.size.width -= amount;
    }

    //
    // Set the new position.
    //
    [self setFrame:rect display:YES];
}


//
// Place the window where the user has asked for it to be placed.
//
- (void)performPlacement
{
    NSRect	wRect = [self frame], sRect = [[self deepestScreen] frame], rect;
    NSPoint	origin;


    //
    // Account for the menu bar
    //
    if ([NSMenu menuBarVisible] == YES)
	sRect.size.height -= 22;

    //
    // This whole group handles "tight" placement, where it is sitting on
    // the edge of the screen.
    //
    if (placement == BMOverlayPlacementTopLeftTight)
	origin = NSMakePoint(0, sRect.size.height - wRect.size.height);
    else if (placement == BMOverlayPlacementTopRightTight)
	origin = NSMakePoint(sRect.size.width - wRect.size.width, sRect.size.height - wRect.size.height);
    else if (placement == BMOverlayPlacementBottomLeftTight)
	origin = NSMakePoint(0, 0);
    else if (placement == BMOverlayPlacementBottomRightTight)
	origin = NSMakePoint(sRect.size.width - wRect.size.width, 0);
    else if (placement == BMOverlayPlacementTopTight)
	origin = NSMakePoint((sRect.size.width - wRect.size.width) / 2, sRect.size.height - wRect.size.height);
    else if (placement == BMOverlayPlacementBottomTight)
	origin = NSMakePoint((sRect.size.width - wRect.size.width) / 2, 0);
    else if (placement == BMOverlayPlacementLeftTight)
	origin = NSMakePoint(0, (sRect.size.height - wRect.size.height) / 2);
    else if (placement == BMOverlayPlacementRightTight)
	origin = NSMakePoint(sRect.size.width - wRect.size.width, (sRect.size.height - wRect.size.height) / 2);
    else {
	//
	// Configure the window to be in the center of an area.
	//
	if (placement == BMOverlayPlacementTopLeft)
	    rect = NSMakeRect(0, sRect.size.height / 2, sRect.size.width / 2, sRect.size.height / 2);
	else if (placement == BMOverlayPlacementTopRight)
	    rect = NSMakeRect(sRect.size.width / 2, sRect.size.height / 2, sRect.size.width / 2, sRect.size.height / 2);
	else if (placement == BMOverlayPlacementBottomRight)
	    rect = NSMakeRect(sRect.size.width / 2, 0, sRect.size.width / 2, sRect.size.height / 2);
	else if (placement == BMOverlayPlacementBottomLeft)
	    rect = NSMakeRect(0, 0, sRect.size.width / 2, sRect.size.height / 2);
	else if (placement == BMOverlayPlacementTop)
	    rect = NSMakeRect(0, sRect.size.height / 2, sRect.size.width, sRect.size.height / 2);
	else if (placement == BMOverlayPlacementBottom)
	    rect = NSMakeRect(0, 0, sRect.size.width, sRect.size.height / 2);
	else if (placement == BMOverlayPlacementLeft)
	    rect = NSMakeRect(0, 0, sRect.size.width / 2, sRect.size.height);
	else if (placement == BMOverlayPlacementRight)
	    rect = NSMakeRect(sRect.size.width / 2, 0, sRect.size.width / 2, sRect.size.height);
	else
	    rect = sRect;

	//
	// Set the location of the window to be centered.
	//
	origin = [self frame].origin;
	origin.x = (rect.origin.x + (rect.size.width - wRect.size.width) / 2);
	origin.y = (rect.origin.y + (rect.size.height - wRect.size.height) / 2);
    }

    //
    // Adjust for the relative position of the screen.
    //
    origin.x += sRect.origin.x;
    origin.y += sRect.origin.y;

    finalPosition = NSMakeRect(origin.x, origin.y, wRect.size.width, wRect.size.height);
}


//
// Set the number of seconds to wait before going away.
//
- (void)setVisibleTime:(float)seconds
{
    visibleTime = seconds;
}


//
// Get the number of seconds to wait before going away.
//
- (float)visibleTime
{
    return visibleTime;
}


//
// Set the placement of this window.  It can either be centered on the screen,
// centered in one of the four segments of the screen, or tight in one of the
// four corners of the screen.
//
- (void)setPlacement:(BMOverlayPlacementType)aPlacement
{
    placement = aPlacement;
}


//
// Retrieve the value of the placement.
//
- (BMOverlayPlacementType)placement
{
    return placement;
}


//
// Sets wether this window crawls in.  If placed tight on an edge then
// the window can crawl in from the edge of the screen.
//
- (void)setCrawlsIn:(BOOL)crawl
{
    crawlsIn = crawl;
}


//
// Retrieves the crawl in state.
//
- (BOOL)crawlsIn
{
    return crawlsIn;
}


//
// Sets wether this window crawls out.  If placed tight on an edge then
// the window can crawl out to the edge of the screen.
//
- (void)setCrawlsOut:(BOOL)crawl
{
    crawlsOut = crawl;
}


//
// Retrieves the crawl out state.
//
- (BOOL)crawlsOut
{
    return crawlsOut;
}


//
// Sets the crawl speed for this window. The higher the number the slower the crawl.
// This is an accelarated curve, it crawls in fast and then slows down.  The number
// given should be higher to slow the crawl down and lower to speed it up.  15 is a
// good number for most cases.
//
- (void)setCrawlSpeed:(unsigned int)speed
{
    crawlSpeed = speed;
}


//
// Retrieve the value of the crawl speed.
//
- (unsigned int)crawlSpeed
{
    return crawlSpeed;
}


@end
